package com.yvan.serverless;

import com.yvan.YvanUtil;
import com.yvan.platform.JsonWapper;
import com.yvan.serverless.jdbc.TransactionService;
import com.yvan.serverless.mvc.Model;
import com.yvan.serverless.mvc.ModelOps;
import com.yvan.serverless.script.ScriptService;
import com.yvan.serverless.utils.FileUtilExt;
import com.yvan.serverless.utils.HttpUtilExt;
import com.yvan.serverless.utils.Tuple;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.graalvm.polyglot.PolyglotException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionStatus;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;

@Slf4j
public class ServerLessServlet extends HttpServlet {
    private static final String BODY_PARSED = "__BODY_PARSED__";

    @Autowired
    private ServerLessProperties properties;

    @Autowired
    private ScriptService scriptService;

    @Autowired
    private TransactionService transactionService;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
        String url = request.getRequestURI();

        // 后端TS /!
        if (url.startsWith(properties.getTypescript().getUrl())) {
            url = url.substring(properties.getTypescript().getUrl().length());
        }

        if (url.equalsIgnoreCase("/~")) {
            try {
                scriptService.refresh();
            } catch (IllegalStateException e) {
                try {
                    HttpUtilExt.writeToResponse(resp, "application/json;charset=utf-8", YvanUtil.toJson(
                            ModelOps.newFail(e.getMessage())
                    ));
                } catch (Exception e2) {
                    log.error("error", e2);
                }
                return;
            }

            try {
                HttpUtilExt.writeToResponse(resp, "application/json;charset=utf-8", YvanUtil.toJson(
                        ModelOps.newSuccess()
                ));
            } catch (Exception e) {
                log.error("error" + e);
            }

        } else if ("/ok".equalsIgnoreCase(url)) {
            // 探针
            HttpUtilExt.writeToResponse(resp, "text/plain", "OK");
            return;
        }

        url = FileUtilExt.normalizePath(url);
        if (url.toLowerCase().endsWith(".js")) {
            // 请求服务端声明(客户端 requirejs 中的 @server)
            String content = scriptService.createBroker(url);
            HttpUtilExt.writeToResponse(resp, "text/javascript;charset=utf-8", content);
            return;
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
        String url = request.getRequestURI();

        url = FileUtilExt.normalizePath(url);
        if (url.startsWith(properties.getTypescript().getUrl())) {
            url = url.substring(properties.getTypescript().getUrl().length());
        }

        Object result;
        try {
            result = execute(url);
        } catch (PolyglotException e2) {
            log.error("PolyglotException", e2);

            ModelOps modelOps = new ModelOps();
            modelOps.setSuccess(false);
            modelOps.setMsg(e2.getMessage());
            modelOps.setData(null);

            result = modelOps;
            resp.setStatus(500);

        } catch (Exception e) {
            log.error("Exception", e);

            ModelOps modelOps = new ModelOps();
            modelOps.setSuccess(false);
            modelOps.setMsg(e.getMessage());
            modelOps.setData(null);

            result = modelOps;
            resp.setStatus(500);
        }
        try {
            if (result instanceof String) {
                HttpUtilExt.writeToResponse(resp, "text/plain;charset=utf-8", (String) result);

            } else if (result != null) {
                HttpUtilExt.writeToResponse(resp, "application/json;charset=utf-8", YvanUtil.toJson(result));
            }

        } catch (Exception e) {
            log.error("error", e);
        }
    }


    /**
     * 执行脚本
     */
    public Object execute(String path) {
        Tuple<String, String> tuplePath = parsePath(path);

        if (tuplePath.v1.contains("\"") || tuplePath.v1.contains("'")) {
            return Model.newFail("非法请求路径");
        }

        if (tuplePath.v1.contains("..")) {
            return Model.newFail("非法请求路径");
        }

        if (tuplePath.v1.startsWith("/")) {
            tuplePath.v1 = "." + tuplePath.v1;
        }

        if (!tuplePath.v1.startsWith("./")) {
            tuplePath.v1 = "./" + tuplePath.v1;
        }

        JsonWapper jw = parseToJsonWapper();
        Object[] argsBody = jw.asObject(Object[].class, "args");

        TransactionStatus transactionStatus = transactionService.begin();
        try {
            Object obj = scriptService.execute(tuplePath.v1, tuplePath.v2, argsBody);
            transactionService.commit(transactionStatus);
            return obj;

        } catch (Exception e) {
            transactionService.rollback(transactionStatus);
            throw e;
        }
    }

    @SneakyThrows
    JsonWapper parseToJsonWapper() {
        HttpServletRequest request = HttpUtilExt.currentRequest();
        if (request == null) {
            return new JsonWapper();
        }

        if (request.getHeader("content-type").startsWith("multipart/form-data")) {
            return new JsonWapper();
        }

        Object jwRaw = request.getAttribute(BODY_PARSED);
        if (jwRaw == null) {
            try (InputStream is = request.getInputStream()) {
                JsonWapper jw;
                try {
                    jw = new JsonWapper(is);
                } catch (Exception e) {
                    jw = new JsonWapper();
                }
                request.setAttribute(BODY_PARSED, jw);
                return jw;
            }
        }

        return (JsonWapper) jwRaw;
    }

    /**
     * 根据路径 path，获取 JS模块[tuple.v1] 和方法名[tuple.v2]
     */
    private Tuple<String, String> parsePath(String path) {
        int pos = path.lastIndexOf("@");
        if (pos < 0) {
            throw new RuntimeException("非法路径:" + path);
        }
        return new Tuple<>(
                path.substring(0, pos),
                path.substring(pos + 1)
        );
    }

}
